package at.sternwerk.domainfinder.data.xmlrpc;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.commons.httpclient.HttpClient;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.client.XmlRpcCommonsTransportFactory;
import org.apache.xmlrpc.common.XmlRpcStreamConfig;

import at.sternwerk.domainfinder.data.xmlrpc.types.ArrayElement;
import at.sternwerk.domainfinder.data.xmlrpc.types.Element;
import at.sternwerk.domainfinder.data.xmlrpc.types.MapElement;

public class Connector {
  private final XmlRpcClient client = new XmlRpcClient();
  private boolean loggedIn;

  private static final String SERVER_URL = "https://api.ote.domrobot.com/xmlrpc/"; // TODO get from config in resources
  private XmlRpcClientConfigImpl serverConfig = new XmlRpcClientConfigImpl();

  public Connector() {
    super();
    setCertificate();
    configureClient();
    configureServer();
  }

  private void setCertificate() {
    System.setProperty("javax.net.ssl.trustStore", getClass().getResource("/cacerts").getPath());
  }

  private void configureClient() {
    XmlRpcCommonsTransportFactory transport = new XmlRpcCommonsTransportFactory(client);
    transport.setHttpClient(new HttpClient());
    client.setTransportFactory(transport);
  }

  private void configureServer() {
    String version = "1.0";
    URL serverUrl = null;
    try {
      serverUrl = new URL(SERVER_URL);
    } catch (MalformedURLException e) {
      assert false : "should not happen - would mean that the server URL was malformed";
    }

    serverConfig.setServerURL(serverUrl);
    serverConfig.setEncoding(XmlRpcStreamConfig.UTF8_ENCODING);
    serverConfig.setConnectionTimeout(2000);
    serverConfig.setUserAgent("DomRobot/" + version + " (Java " + System.getProperty("java.version") + ")");
    serverConfig.setEnabledForExtensions(true);
  }

  public boolean logout() throws XmlRpcException {
    HashMap<String, Object> map = new HashMap<String, Object>();
    Vector<HashMap<String, Object>> attr = new Vector<HashMap<String, Object>>();
    map.put("lang", "eng");
    attr.add(map);
    Map<String, Object> response = (Map<String, Object>) client.execute(serverConfig, "account.logout", attr);
    if (responseIsOk(response)) {
      loggedIn = false;
      return true;
    }
    return false;
  }

  /**
   * Logs in to the server with the given username and password. Must be called before any other method can be called.
   */
  public boolean login(String username, String password) throws XmlRpcException {
    Vector<HashMap<String, Object>> attr = new Vector<HashMap<String, Object>>();
    HashMap<String, Object> map = new HashMap<String, Object>();

    map.put("user", username);
    map.put("pass", password);
    map.put("lang", "eng");
    attr.add(map);

    Map<String, Object> response = (Map<String, Object>) client.execute(serverConfig, "account.login", attr);
    if (responseIsOk(response)) {
      loggedIn = true;
      return true;
    }
    return false;
  }

  private boolean responseIsOk(Map<String, Object> response) {
    if (Long.valueOf(response.get("code").toString()).longValue() != 1000L) {
      // TODO show error
      System.out.println(response.get("reason"));
      System.out.println(response.get("reasonCode"));
      System.out.println(response.get("msg"));
      return false;
    }
    return true;
  }

  public HashMap<String, Element> getUserInfo() throws XmlRpcException {
    assert (loggedIn) : "must be logged in to call this method";

    List<Object> attr = Collections.EMPTY_LIST;
    Map<String, Object> res = (Map<String, Object>) client.execute(serverConfig, "account.info", attr);
    HashMap<String, Element> result = new HashMap<String, Element>();
    result.putAll((new MapElement(res)).getMap());
    return result;
  }

  public ArrayElement checkDomain(String domainName) throws XmlRpcException {
    assert (loggedIn) : "must be logged in to call this method";
    return checkDomain(domainName, "2");
  }

  public ArrayElement checkDomain(String domainName, String wide) throws XmlRpcException {
    assert (loggedIn) : "must be logged in to call this method";

    List<HashMap<String, Object>> attr = new ArrayList<HashMap<String, Object>>();
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("domain", domainName);
    map.put("lang", "eng");
    if (wide != null) map.put("wide", wide);
    attr.add(map);
    Map<String, Object> response = (Map<String, Object>) client.execute(serverConfig, "domain.check", attr);
    if ( !responseIsOk(response)) { return null; }

    HashMap<String, Element> result = new HashMap<String, Element>();
    result.putAll((new MapElement(response)).getMap());

    MapElement resData = (MapElement) result.get("resData");
    return (ArrayElement) resData.get("domain");
  }

  public ArrayElement getAllNS() throws XmlRpcException {
    assert (loggedIn) : "must be logged in to call this method";

    return getNS(null);
  }

  public ArrayElement getNS(String domainName) throws XmlRpcException {
    assert (loggedIn) : "must be logged in to call this method";

    Vector<HashMap<String, Object>> attr = new Vector<HashMap<String, Object>>();
    HashMap<String, Object> map = new HashMap<String, Object>();
    if (domainName != null) map.put("domain", domainName);
    map.put("lang", "eng");
    // map.put("pagelimit","100"); //max. entries per page (default: 20)
    // map.put("page","2"); //- page of the list (default: 1)
    attr.add(map);
    Map<String, Object> response = (Map<String, Object>) client.execute(serverConfig, "nameserver.list", attr);
    if ( !responseIsOk(response)) { return null; }

    HashMap<String, Element> result = new HashMap<String, Element>();
    result.putAll((new MapElement(response)).getMap());
    MapElement resData = (MapElement) result.get("resData");
    ArrayElement domains = (ArrayElement) resData.get("domains");
    return domains;
  }
}
